﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using Z.EntityFramework.Plus;
using System.Linq.Dynamic;

namespace SimpleModel.Context
{
    public static class DbContextExtender
    {
        /// <summary>
        /// 获取记录总数
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <returns></returns>
        public static int ACount<TEntityType>(this DbContext mContext) where TEntityType : class
        {
            try
            {
                return mContext.Set<TEntityType>().Count();

            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return 0;
            }

        }

        /// <summary>
        /// 获取所有记录
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <returns></returns>
        public static IQueryable<TEntityType> GetAll<TEntityType>(this DbContext mContext) where TEntityType : class
        {
            try
            {
                return mContext.Set<TEntityType>();
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return null;
            }
        }

        /// <summary>
        /// 根据主键获取记录
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="keyValues">主键</param>
        /// <returns></returns>
        public static TEntityType FindOne<TEntityType>(this DbContext mContext, params object[] keyValues) where TEntityType : class
        {
            try
            {
                return mContext.Set<TEntityType>().Find(keyValues);
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return null;
            }
        }

        /// <summary>
        /// 根据查询条件获取记录
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="expression">查询条件</param>
        /// <returns></returns>
        public static IQueryable<TEntityType> FindByQuery<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> expression) where TEntityType : class
        {
            try
            {
                return mContext.Set<TEntityType>().Where(expression);
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return null;
            }
        }


        /// <summary>
        /// 查询、排序、分页
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="expression">查询条件</param>
        /// <param name="asc">升序、降序</param>
        /// <param name="keySelector">排序选择方法</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="total">总条数</param>
        /// <returns></returns>
        public static IQueryable<TEntityType> Search<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> expression, bool asc, Expression<Func<TEntityType, object>> keySelector, int pageIndex, int pageSize, ref int total) where TEntityType : class
        {
            try
            {
                IQueryable<TEntityType> q = null;
                if (asc)
                {
                    q = mContext.Set<TEntityType>().Where(expression).OrderBy(keySelector);
                }
                else
                {
                    q = mContext.Set<TEntityType>().Where(expression).OrderByDescending(keySelector);
                }

                var q1 = q.DeferredCount().FutureValue();
                var q2 = q.Skip((pageIndex - 1) * pageSize).Take(pageSize).Future();
                total = q1.Value;
                return q2.AsQueryable();
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                total = 0;
                return null;
            }
        }

        /// <summary>
        /// 查询、排序、分页
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="expression">查询条件</param>
        /// <param name="orderString">排序字符串，同sql</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="total">总条数</param>
        /// <returns></returns>
        public static IQueryable<TEntityType> Search<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> expression, string orderString, int pageIndex, int pageSize, ref int total) where TEntityType : class
        {
            try
            {
                IQueryable<TEntityType> q = null;
                q = mContext.Set<TEntityType>().Where(expression).OrderBy(orderString);
                var q1 = q.DeferredCount().FutureValue();
                var q2 = q.Skip((pageIndex - 1) * pageSize).Take(pageSize).Future();
                total = q1.Value;
                return q2.AsQueryable();
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                total = 0;
                return null;
            }
        }

        /// <summary>
        /// 添加记录（待提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static void AddOne<TEntityType>(this DbContext mContext, TEntityType entity) where TEntityType : class
        {
            mContext.Set<TEntityType>().Add(entity);
        }

        /// <summary>
        /// 添加多条记录（待提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entityList">实体对象列表</param>
        /// <returns></returns>
        public static void AddMultiple<TEntityType>(this DbContext mContext, List<TEntityType> entityList) where TEntityType : class
        {
            mContext.Set<TEntityType>().AddRange(entityList);
        }

        /// <summary>
        /// 更新记录（待提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static void UpdateOne<TEntityType>(this DbContext mContext, TEntityType entity) where TEntityType : class
        {
            mContext.Entry(entity).State = EntityState.Modified;
        }

        /// <summary>
        /// 删除记录（待提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static void DeleteOne<TEntityType>(this DbContext mContext, TEntityType entity) where TEntityType : class
        {
            mContext.Entry(entity).State = EntityState.Deleted;
        }

        /// <summary>
        /// 删除多条记录（待提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="expression">删除条件</param>
        /// <returns></returns>
        public static void DeleteMultiple<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> expression) where TEntityType : class
        {
            try
            {
                var models = mContext.Set<TEntityType>().Where(expression).ToList();
                models.ForEach(x =>
                {
                    mContext.Entry(x).State = EntityState.Deleted;
                });
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
            }
        }

        /// <summary>
        /// 添加记录（提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static bool AddOneAndSave<TEntityType>(this DbContext mContext, TEntityType entity)
            where TEntityType : class
        {
            try
            {
                mContext.Set<TEntityType>().Add(entity);
                mContext.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return false;
            }
        }

        /// <summary>
        /// 添加多条记录（提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entityList">实体对象列表</param>
        /// <returns></returns>
        public static bool AddMultipleAndSave<TEntityType>(this DbContext mContext, List<TEntityType> entityList) where TEntityType : class
        {
            try
            {
                mContext.Set<TEntityType>().AddRange(entityList);
                mContext.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return false;
            }
        }

        /// <summary>
        /// 更新记录（提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static bool UpdateOneAndSave<TEntityType>(this DbContext mContext, TEntityType entity) where TEntityType : class
        {
            return UpdateOneAndSave(mContext, entity, true);
        }

        /// <summary>
        /// 更新记录（提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <param name="updateAll">是否完全更新</param>
        /// <param name="propertyNames">需要更新的字段</param>
        /// <returns></returns>
        public static bool UpdateOneAndSave<TEntityType>(this DbContext mContext, TEntityType entity, bool updateAll, params string[] propertyNames) where TEntityType : class
        {
            try
            {
                if (updateAll)
                {
                    mContext.Entry(entity).State = EntityState.Modified;
                    mContext.SaveChanges();
                    return true;
                }
                else if (propertyNames != null && propertyNames.Length > 0)
                {
                    mContext.Configuration.ValidateOnSaveEnabled = false;
                    DbEntityEntry dbEntity = mContext.Entry(entity);
                    dbEntity.State = EntityState.Unchanged;
                    foreach (var propertyName in propertyNames)
                    {
                        dbEntity.Property(propertyName).IsModified = true;
                    }
                    int num = mContext.SaveChanges();
                    mContext.Configuration.ValidateOnSaveEnabled = true;
                    return num > 0;
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return false;
            }
        }

        /// <summary>
        /// 删除记录（提交）
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static bool DeleteOneAndSave<TEntityType>(this DbContext mContext, TEntityType entity) where TEntityType : class
        {
            try
            {
                mContext.Entry(entity).State = EntityState.Deleted;
                mContext.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return false;
            }
        }
        /// <summary>
        /// 提交修改
        /// </summary>
        /// <param name="mContext">数据库上下文对象</param>
        /// <returns></returns>
        public static bool Save(this DbContext mContext)
        {
            try
            {
                mContext.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return false;
            }
        }

        /// <summary>
        /// 更新多条记录（直接提交）只用于sqlserver
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="queryExpression">查询条件</param>
        /// <param name="updateExpression">更新操作</param>
        /// <returns></returns>
        [Obsolete("is only applicable to sqlserver")]
        public static int UpdateMultipleAndSave<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> queryExpression, Expression<Func<TEntityType, TEntityType>> updateExpression) where TEntityType : class
        {
            try
            {
                return mContext.FindByQuery(queryExpression).Update(updateExpression);
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return 0;
            }
        }

        /// <summary>
        /// 删除多条记录（直接提交）只用于sqlserver
        /// </summary>
        /// <typeparam name="TEntityType">实体类型</typeparam>
        /// <param name="mContext">数据库上下文对象</param>
        /// <param name="expression">查询条件</param>
        /// <returns></returns>
        [Obsolete("is only applicable to sqlserver")]
        public static int DeleteMultipleAndSave<TEntityType>(this DbContext mContext, Expression<Func<TEntityType, bool>> expression) where TEntityType : class
        {
            try
            {
                return mContext.FindByQuery(expression).Delete();
            }
            catch (Exception ex)
            {
                HandleUtil.Handle(ex, "数据访问错误");
                return 0;
            }
        }
    }

    public class HandleUtil
    {
        public static void Handle(Exception ex, string msg)
        {
            
        }
    }

    public static class OtherExtender
    {
        public static IQueryable<T> OrderByString<T>(this IQueryable<T> query, string orderString, params object[] values)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }
            return query.OrderBy(orderString, values);
        }
    }
    public static class PredicateExtensions
    {
        public static Expression<Func<T, bool>> True<T>()
        {
            return f => true;
        }

        public static Expression<Func<T, bool>> False<T>()
        {
            return f => false;
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
        {
            var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>(Expression.Or(expression1.Body, invokedExpression), expression1.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
        {
            var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters);
        }
    }
}
